\subsection{Comparação com JDBC}
\label{sec:disjdbccmp}

Esta subsecção discute e analisa os resultados apresentados na secção \ref{sec:resjdbccmp}.

Como já foi mencionado no capítulo dos resultados, todas as soluções apresentadas demonstraram um tempo de preparação muito melhor do que a solução $MSJDBC$. 
Este era o resultado esperado, na medida em que nesta solução tem que se criar uma statement e um result set para para thread, enquanto que as restantes soluções possibilitam a partilha do mesmo objecto result set.
Isto significa que num ambiente multithreaded com $n$ threads em execução e em que o tempo de criação de um result set é dado por $c_{RS}$, o tempo total do tempo de preparação para $MSJDBC$ será de $n \times c_{RS}$, enquanto que em $CJDBC$ e em $WJDBC$ será de apenas $c_{RS}$.
Os resultados práticos comprovam esta teoria na medida em que o rácio entre os tempos de preparação do $MSJDBC$ e as outras soluções é numericamente aproximado à quantidade de threads utilizado para efectuar a medição (veja-se por exemplo a Figura \ref{fig:jdbccmp-MSJDBC-WJDBC-Remocao}).
Esta relação não se verifica para a solução $CJDBC_S$ porque esta é a única que na preparação carrega os dados do result set para o cache. As outras soluções apenas criam o result set (declaração e abertura do cursor do servidor).
A vantagem do melhor tempo de preparação revelou-se importante em diversas situações, porque onde por vezes existia uma ligeira vantagem do $MSJDBC$, esta foi anulada com a ajuda do tempo de preparação. 
Por exemplo na comparação entre $MSJDBC$ e $CJDBC_I$ para a Actualização (fig. \ref{fig:jdbccmp-MSJDBC-CJDBC_I-Actualizacao}), que para quantidades de threads e valores de número de linhas maiores o $MSJDBC$ era cerca de 10\% mais rápido, no tempo total a vantagem vai para o $CJDBC_I$ sendo cerca de 30\% mais rápido do que $MSJDBC$.
\hfill \\

% CJDBC_I
A solução ${\bf CJDBC_I}$ revelou-se mais eficiente do que o $MSJDBC$ em todos os contextos, com destaque para os contextos de modificação (Actualização, Inserção e Remoção), em que a tendência é haver mais vantagem para o $CJDBC_I$ quantos mais threads estiverem em execução. Já no contexto da Leitura, embora exista uma clara vantagem para o $CJDBC_I$, a tendência não se verifica pois existe um ligeiro declínio em favor do desempenho do $MSJDBC$. 
Esta situação justifica-se pelo facto de o cursor com cache individual, do ponto de vista do cliente, ter um peso semelhante ao $MSJDBC$ pois cada thread possui em cache a totalidade do \gls{dataset}. Depois como se pode ver no tempo de execução da Leitura (fig. \ref{fig:jdbccmp-MSJDBC-CJDBC_I-Leitura}), a implementação da Microsoft é simplesmente mais eficiente do que a realizada neste trabalho.
No entanto é importante ressalvar que mesmo com a tendência para diminuir o desempenho do $CJDBC_I$ face ao $MSJDBC$ com o aumento da quantidade de threads, é na Leitura que o $CJDBC_I$ demonstra maior superioridade sendo cerca de 35\% a 70\% mais rápido do que $MSJDBC$.
No contexto da inserção verificou-se muito equilíbrio, principalmente nos tempos de execução em que os resultados de $MSJDBC$ e $CJDBC_I$ são muito próximos. No tempo total consegue-se notar uma pequena superioridade de $CJDBC_I$ devido ao bom desempenho do tempo de preparação.
Uma outra nota vai para o facto de os resultados para os contextos da Actualização e da Remoção serem semelhantes. É compreensível que tal se suceda pois tirando o diferente valor para \emph{optype} do \glssymbol{rpc} sp\_cursor (ver \ref{sec:spcursor}), a implementação dos métodos de actualização e remoção semelhantes. Ainda assim no contexto da remoção não há actualização de valores das colunas pelo que o desempenho do $CJDBC_I$ neste contexto é melhor do que na actualização.
\hfill \\

%CJDBC_S
A solução ${\bf CJDBC_S}$ também apresenta um desempenho claramente superior ao desempenho de $MSJDBC$, melhor até porque apesar de não apresentar um tempo de preparação tão bom, ao nível da execução é mais eficiente. O tempo de preparação não é tão bom, porque ao contrário do $MSJDBC$ e do $CJDBC_I$, o $CJDBC_S$ constrói o cache no momento da criação dos cursores, refletindo-se o peso dessa operação no tempo de preparação em vez do tempo de execução.
É também por esta razão que apresenta melhores resultados para a execução, pois uma vez que já tem os dados em cache não necessita de requisitar as linhas do \gls{dataset}, operação essa que se revela bastante penosa para o desempenho.
Este aspecto é claramente visível para o contexto da Leitura em que o $CJDBC_S$ consegue ser no mínimo 4 vezes mais rápido do que $MSJDBC$ e no máximo quase 18 vezes, e para além disso o desempenho comparativo melhora à medida que a quantidade de threads aumenta, ao contrário do $CJDBC_I$ cujo desempenho comparativo diminui nessa situação (ver Figura \ref{fig:jdbccmp-cacheread}).

\begin{figure}[t] 
  \centering 
  \caption{\small Comparação entre $CJDBC$ e $MSJDBC$, no contexto {\bf Leitura} } 
  \label{fig:jdbccmp-cacheread} 
  \subfloat{\includegraphics[angle=-90,width=0.333333333\textwidth]{images/results/jdbccmp-MSJDBC-CJDBC_I-Leitura-ExecT}} 
  \subfloat{\includegraphics[angle=-90,width=0.333333333\textwidth]{images/results/jdbccmp-MSJDBC-CJDBC_S-Leitura-ExecT}}
\end{figure}

Queria aqui referir um aspecto em relação ao contexto de Actualização. Como se pode ver na Figura \ref{fig:jdbccmp-isupdate} o desempenho comparativo do $CJDBC_S$ em relação ao $MSJDBC$ é melhor do que o do $CJDBC_I$. Mas porquê? Até se poderia pensar que aconteceria precisamente o contrário, pois o cursor de cache partilhado tem de obter acesso exclusivo ao cache para o actualizar, enquanto que o cache individual pode actualizar o seu cache sem essa necessidade. A razão surge do facto que o cache individual tem a necessidade de mover o cursor do servidor antes de efectuar a actualização, já o mesmo não acontece com o cursor com cache partilhado.

\begin{figure}[t] 
  \centering 
  \caption{\small Comparação entre $CJDBC$ e $MSJDBC$, no contexto {\bf Actualização} } 
  \label{fig:jdbccmp-isupdate} 
  \subfloat{\includegraphics[angle=-90,width=0.333333333\textwidth]{images/results/jdbccmp-MSJDBC-CJDBC_I-Actualizacao-ExecT}} 
  \subfloat{\includegraphics[angle=-90,width=0.333333333\textwidth]{images/results/jdbccmp-MSJDBC-CJDBC_S-Actualizacao-ExecT}}
\end{figure}

A actualização de um result set no SQL Server a partir de um cursor só é possível ao fim de se carregar (\gls{fetch}) algumas linhas no \emph{buffer} do cursor\footnotemark, e é mesmo lançado um erro caso o buffer esteja vazio. Na operação de fetch o servidor envia as linhas do buffer ao cliente, que depois cria o seu próprio cache. O índice para actualizar uma linha do result set é relativa a esse buffer e não ao result set. Isso quer dizer que o cliente pode pedir para actualizar, por exemplo, a linha 3 e estar a actualizar a linha 60 do result set.
\footnotetext{Na documentação do protocolo é utilizada a designação de \emph{buffer} em vez de cache.}

O cursor com cache individual não pode assumir que tem todas as linhas do result set, logo assume que a sua noção de cache é diferente da noção do servidor, e por essa razão move o cursor do servidor antes de actualizar, através de um UPDATE ABSOLUTE em vez de um UPDATE normal. A diferença é que no UPDATE, o cursor utiliza o seu buffer para actualizar os dados da tabela, enquanto que no UPDATE ABSOLUTE a modificação é realizada nas tabelas. Já no cursor com cache partilhado os índices utilizados para identificar as linhas são os mesmos nos dois lados e inalteráveis, pelo que se pode actualizar directamente usando o cursor servidor.

Tal como se havia verificado com $CJDBC_I$, o desempenho de $CJDBC_S$ e $MSJDBC$ para a Inserção, são próximos.
\hfill \\

%WJDBC
A solução ${\bf WJDBC}$ também revelou melhor desempenho do que $MSJDBC$ em todos os contextos, voltando a revelar uma relação directa entre o número de threads e o ganho de desempenho para o tempo de preparação, tal como se verificou com $CJDBC_I$. Ao nível da preparação as soluções $WJDBC$ e $CJDBC_I$ são semelhantes na medida em que ambas essencialmente o que fazem é abrir e alocar um cursor do servidor que por sua vez cria um result set.

Nos contextos da Actualização e da Remoção os resultados foram semelhantes, porque tal como foi explicado para $CJDBC_I$ as duas operações têm uma implementação semelhante. No entanto, $WJDBC$ tem maior vantagem sobre $MSJDBC$ do que tem $CJDBC_I$, mas isto é um assunto a discutir na comparação com $WJDBC$ na secção \ref{sec:diswjdbccmp}.

A operação em que $WJDBC$ mais se destacou foi a Leitura. Para uma quantidade reduzida de threads a vantagem é pequena, mas para quantidades maiores o desempenho de $WJDBC$ é muito superior ao de $MSJDBC$, chegando a ser 10 mais rápido. A razão principal para este resultado tem a ver com as $n$ cópias do result set que o $MSJDBC$ tem que efectuar do result set.
Na preparação o $MSJDBC$ tem que criar $n$ vezes mais cursores, e como é na execução que as linhas são realmente carregadas em cache, na execução têm que ser carregados $n$ vezes mais result sets. O resultado final é a grande superioridade de desempenho do $WJDBC$ sobre o $MSJDBC$.

Mais uma vez verifica-se que para o contexto da Inserção existe um maior equilíbrio nos resultados não existindo uma vantagem clara para alguma das soluções.
